package com.rnkrsoft.suyan.config.gateway.services.impl;

import com.rnkrsoft.message.MessageFormatter;
import com.rnkrsoft.logtrace4j.ErrorContextFactory;
import com.rnkrsoft.suyan.config.gateway.services.ConfigService;
import com.rnkrsoft.suyan.config.internal.enums.*;
import com.rnkrsoft.suyan.config.protocol.v2.*;
import com.rnkrsoft.suyan.config.server.config.dao.*;
import com.rnkrsoft.suyan.config.server.config.entity.*;
import com.rnkrsoft.utils.DateUtils;
import com.rnkrsoft.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Created by woate on 2018/6/17.
 */
@Slf4j
@Service
public class ConfigServiceImpl implements ConfigService {
    public static final String ENCRYPT_PARAM = "AES/BASE64://";
    @Autowired
    ProjectInfoDAO projectDAO;
    @Autowired
    ProjectDependencyDAO projectDependencyDAO;
    @Autowired
    VersionInfoDAO versionDAO;
    @Autowired
    EnvironmentInfoDAO environmentDAO;
    @Autowired
    MachineInfoDAO machineDAO;
    @Autowired
    ParamInfoDAO paramDAO;
    @Autowired
    FileInfoDAO fileDAO;

    /**
     * 1.首先将当前项目存在继承的项目的所有参数查询出来
     * 2.将当前项目的所有参数查询出来，参数中为继承参数的则在集合1中按名称获取参数
     * 3.最后的结果为该项目的参数
     *
     * @param request
     * @return
     */
    @Override
    public FetchResponse fetch(FetchRequest request) {
        FetchResponse response = new FetchResponse();
        List<ProjectInfoEntity> projectEntities = projectDAO.selectAnd(ProjectInfoEntity.builder()
                .groupId(request.getGroupId())
                .artifactId(request.getArtifactId())
                .build());
        if (projectEntities.isEmpty()) {
            //项目不存在
            throw ErrorContextFactory.instance()
                    .message("project [{}:{}] is not exists!", request.getGroupId(), request.getArtifactId())
                    .runtimeException();
        }
        if (projectEntities.size() > 1) {
            //项目不存在
            throw ErrorContextFactory.instance()
                    .message("exists too many project [{}:{}]!", request.getGroupId(), request.getArtifactId())
                    .runtimeException();
        }
        ProjectInfoEntity projectEntity = projectEntities.get(0);
        List<VersionInfoEntity> versionInfoEntities = versionDAO.selectAnd(VersionInfoEntity.builder().projectId(projectEntity.getProjectId()).build());
        if (versionInfoEntities.isEmpty()) {
            //版本不存在
            throw ErrorContextFactory.instance()
                    .message("version [{}:{}:{}] is not exists!", request.getGroupId(), request.getArtifactId(), request.getVersion())
                    .runtimeException();
        }
        if (versionInfoEntities.size() > 1) {
            //版本不存在
            throw ErrorContextFactory.instance()
                    .message("exists too many version [{}:{}:{}] is not exists!", request.getGroupId(), request.getArtifactId(), request.getVersion())
                    .runtimeException();
        }
        VersionInfoEntity versionInfoEntity = versionInfoEntities.get(0);
        EnvironmentInfoEntity environmentEntity = new EnvironmentInfoEntity();
        environmentEntity.setVersionId(versionInfoEntity.getVersionId());
        environmentEntity.setEnvironmentName(request.getEnv());
        List<EnvironmentInfoEntity> environmentEntities = environmentDAO.selectAnd(environmentEntity);
        if (environmentEntities.isEmpty()) {
            //环境不存在
            throw ErrorContextFactory.instance()
                    .message("env [{}:{}:{}:{}] is not exists!", request.getGroupId(), request.getArtifactId(), request.getVersion(), request.getEnv())
                    .runtimeException();
        }
        if (environmentEntities.size() > 1) {
            //环境不存在
            throw ErrorContextFactory.instance()
                    .message("exists too many env [{}:{}:{}:{}]!", request.getGroupId(), request.getArtifactId(), request.getVersion(), request.getEnv())
                    .runtimeException();
        }
        environmentEntity = environmentEntities.get(0);

        MachineInfoEntity machineEntity = new MachineInfoEntity();
        machineEntity.setEnvironmentId(environmentEntity.getEnvironmentId());
        machineEntity.setMachineName(request.getMachine());
        List<MachineInfoEntity> machineEntities = machineDAO.selectAnd(machineEntity);
        if (machineEntities.isEmpty()) {
            machineEntity.setEnvironmentId(environmentEntity.getEnvironmentId());
            machineEntity.setMachineName("*");
            machineEntities = machineDAO.selectAnd(machineEntity);
            if (machineEntities.isEmpty()) {
                //机器不存在
                throw ErrorContextFactory.instance()
                        .message("machine [{}:{}:{}:{}:{}] is not exists!", request.getGroupId(), request.getArtifactId(), request.getVersion(), request.getEnv(), request.getMachine())
                        .runtimeException();
            }
        }
        if (machineEntities.size() > 1) {
            //机器不存在
            throw ErrorContextFactory.instance()
                    .message("exists too many machine [{}:{}:{}:{}:{}]!", request.getGroupId(), request.getArtifactId(), request.getVersion(), request.getEnv(), request.getMachine())
                    .runtimeException();
        }
        machineEntity = machineEntities.get(0);

        //根据项目查询继承的参数列表
        List<ParamInfoEntity> paramEntities = paramDAO.selectAllParams(projectEntity.getProjectId(), request.getVersion(), request.getEnv(), request.getMachine());
        for (ParamInfoEntity record : paramEntities) {
            String paramValue = record.getParamValue();
            if (YesOrNo.valueOfCode(record.getParamEncrypt()) == YesOrNo.Y){
                paramValue = ENCRYPT_PARAM + Aes.encrypt(machineEntity.getSecurityKey(), Aes.DEFAULT_IV, paramValue);
            }
            ParamObject paramObject = ParamObject.builder()
                    .key(record.getParamName())
                    .value(paramValue)
                    .desc(record.getParamDesc())
                    .enabled(ParamStatusEnum.valueOfCode(record.getParamStatus()) == ParamStatusEnum.NORMAL)
                    .dynamic(YesOrNo.valueOfCode(record.getParamDynamic()) == YesOrNo.Y)
                    .systemProperties(YesOrNo.valueOfCode(record.getParamSystemProperties()) == YesOrNo.Y)
                    .encrypt(YesOrNo.valueOfCode(record.getParamEncrypt()) == YesOrNo.Y)
                    .createUid(record.getParamCreator())
                    .lastUpdateUid(record.getParamLastUpdater())
                    .createTime(DateUtils.toFullString(record.getCreateDate()))
                    .updateTime(DateUtils.toFullString(record.getLastUpdateDate()))
                    .build();
            response.getParams().add(paramObject);
        }
        FileInfoEntity fileEntity = new FileInfoEntity();
        fileEntity.setMachineId(machineEntity.getMachineId());
        List<FileInfoEntity> fileEntities = fileDAO.selectAnd(fileEntity);
        for (FileInfoEntity fileEntity0 : fileEntities) {
            FileObject fileObject = FileObject.builder()
                    .fileFullName(fileEntity0.getFilePath() + "/" + fileEntity0.getFileName())
                    .fileFingerprint(fileEntity0.getFileFingerprint()).build();
            response.getFiles().add(fileObject);
        }
        response.setId(request.getId());
        response.setUpdateTimestamp(machineEntity.getLastUpdateDate().getTime());
        return response;
    }

    @Override
    public GetParamResponse getParam(GetParamRequest request) {
        GetParamResponse response = new GetParamResponse();
        if (request.getKeys().isEmpty()) {
            response.getResult().update(ConfigResponseCode.FAIL);
            return response;
        }
        //根据项目查询继承的参数列表
        List<ParamInfoEntity> paramEntities = paramDAO.selectAllParamsByGavem(request.getGroupId(), request.getArtifactId(), request.getVersion(), request.getEnv(), request.getMachine());
        for (ParamInfoEntity record : paramEntities) {
            String paramValue = record.getParamValue();
            ParamObject paramObject = ParamObject.builder()
                    .key(record.getParamName())
                    .value(paramValue)
                    .desc(record.getParamDesc())
                    .enabled(ParamStatusEnum.valueOfCode(record.getParamStatus()) == ParamStatusEnum.NORMAL)
                    .dynamic(YesOrNo.valueOfCode(record.getParamDynamic()) == YesOrNo.Y)
                    .systemProperties(YesOrNo.valueOfCode(record.getParamSystemProperties()) == YesOrNo.Y)
                    .encrypt(YesOrNo.valueOfCode(record.getParamEncrypt()) == YesOrNo.Y)
                    .createUid(record.getParamCreator())
                    .lastUpdateUid(record.getParamLastUpdater())
                    .createTime(DateUtils.toFullString(record.getCreateDate()))
                    .updateTime(DateUtils.toFullString(record.getLastUpdateDate()))
                    .build();
            response.getParams().add(paramObject);
        }
        return response;
    }

    @Override
    @Transactional
    public PushResponse push(PushRequest request) {
        PushResponse.PushResponseBuilder responseBuilder = PushResponse.builder();
        if (StringUtils.isBlank(request.getUserId())) {
            PushResponse response = responseBuilder.build();
            response.getResult().update(ConfigResponseCode.NO_DATA.getCode(), "请配置用户号");
            return response;
        }
        //1.检查项目信息是否存在，如果存在则检查是否允许推送；如果不存在则创建项目信息
        List<ProjectInfoEntity> projectEntities = projectDAO.selectAnd(ProjectInfoEntity.builder()
                .groupId(request.getGroupId())
                .artifactId(request.getArtifactId())
                .build());
        ProjectInfoEntity projectEntity = null;
        if (projectEntities.isEmpty()) {
            projectEntity = ProjectInfoEntity.builder()
                    .groupId(request.getGroupId())
                    .artifactId(request.getArtifactId())
                    .allowPush(YesOrNo.Y.getCode())
                    .projectStatus(ProjectStatusEnum.NORMAL.getCode())
                    .projectDesc("推送创建")
                    .build();
            projectDAO.insertSelective(projectEntity);
        } else {
            projectEntity = projectEntities.get(0);
        }
        //项目不允许推送
        if (YesOrNo.valueOfCode(projectEntity.getAllowPush()) == YesOrNo.N) {
            PushResponse response = new PushResponse();
            log.error("project [{}:{}:{}] is not allow push!", request.getGroupId(), request.getArtifactId(), request.getVersion());
            String desc = MessageFormatter.format("project [{}:{}:{}] is not allow push!", request.getGroupId(), request.getArtifactId(), request.getVersion());
            response.getResult().update(ConfigResponseCode.FAIL.getCode(), desc);
            return response;
        }
        List<VersionInfoEntity> versionEntities = versionDAO.selectAnd(VersionInfoEntity.builder()
                .projectId(projectEntity.getProjectId())
                .build());
        VersionInfoEntity versionEntity = null;
        if (versionEntities.isEmpty()){
            versionEntity = VersionInfoEntity.builder()
                    .version(request.getVersion())
                    .projectId(projectEntity.getProjectId())
                    .build();
            versionDAO.insertSelective(versionEntity);
        }else{
            versionEntity = versionEntities.get(0);
        }
        //2.检查环境信息是否存在，如果不存在则创建环境信息，默认环境类型为开发环境
        EnvironmentInfoEntity environmentEntity = new EnvironmentInfoEntity();
        environmentEntity.setVersionId(versionEntity.getVersionId());
        //推送只能创建开发环境
        environmentEntity.setEnvironmentName("DEV");
        environmentEntity.setEnvironmentStatus(EnvironmentStatusEnum.NORMAL.getCode());

        List<EnvironmentInfoEntity> environmentEntities = environmentDAO.selectAnd(environmentEntity);
        if (environmentEntities.isEmpty()) {
            environmentEntity.setProjectId(projectEntity.getProjectId());
            environmentEntity.setEnvironmentType(EnvironmentTypeEnum.DEV.getCode());
            environmentEntity.setEnvironmentDesc("推送创建");
            environmentDAO.insertSelective(environmentEntity);
        } else {
            environmentEntity = environmentEntities.get(0);
        }
        //3.检查机器信息是否存在，如果不存在则创建机器信息，需要特殊处理机器名为*的机器
        MachineInfoEntity machineEntity = new MachineInfoEntity();
        machineEntity.setEnvironmentId(environmentEntity.getEnvironmentId());
        machineEntity.setMachineName(request.getMachine());
        List<MachineInfoEntity> machineEntities = machineDAO.selectAnd(machineEntity);
        if (machineEntities.isEmpty()) {
            machineEntity.setProjectId(projectEntity.getProjectId());
            machineEntity.setVersionId(versionEntity.getVersionId());
            machineEntity.setMachineStatus(MachineStatusEnum.NORMAL.getCode());
            machineEntity.setMachineDesc("推送创建");
            machineEntity.setSecurityKey(UUID.randomUUID().toString());
            machineDAO.insertSelective(machineEntity);
        } else {
            machineEntity = machineEntities.get(0);
        }
        //4.检查参数信息是否存在，如果存在则将原参数信息修改为删除状态
        List<ParamInfoEntity> paramEntities = paramDAO.selectAnd(ParamInfoEntity.builder()
                        .projectId(machineEntity.getProjectId())
                        .environmentId(machineEntity.getEnvironmentId())
                        .machineId(machineEntity.getMachineId())
                        .build()
        );
        int newParamCount = 0;
        int newFileCount = 0;
        int updateParamCount = 0;
        int updateFileCount = 0;
        int totalParamCount = 0;
        int totalFileCount = 0;
        Map<String, ParamInfoEntity> existsParams = new HashMap<String, ParamInfoEntity>();
        for (ParamInfoEntity paramInfoEntity : paramEntities) {
            existsParams.put(paramInfoEntity.getParamName(), paramInfoEntity);
        }
        //5.创建参数信息
        for (ParamObject paramObject : request.getParams()) {
            if (existsParams.containsKey(paramObject.getKey())) {
                //参数如果存在，则进行更新操作
                ParamInfoEntity existsParamInfoEntity = existsParams.get(paramObject.getKey());
                ParamInfoEntity paramInfoEntity = ParamInfoEntity.builder()
                        .paramId(existsParamInfoEntity.getParamId())
                        .paramValue(paramObject.getValue())
                        .paramDesc(paramObject.getDesc())
                        .paramDataType(paramObject.getDataType())
                        .paramType(paramObject.getType())
                        .paramExtends(YesOrNo.N.getCode())
                        .paramEncrypt(paramObject.isEncrypt() ? YesOrNo.Y.getCode() : YesOrNo.N.getCode())
                        .paramDynamic(paramObject.isDynamic() ? YesOrNo.Y.getCode() : YesOrNo.N.getCode())
                        .paramSystemProperties(paramObject.isSystemProperties() ? YesOrNo.Y.getCode() : YesOrNo.N.getCode())
                        .paramVersion(existsParamInfoEntity.getParamVersion() + 1)
                        .paramLastUpdater(request.getUserId())
                        .paramStatus(ParamStatusEnum.NORMAL.getCode())
                        .build();
                paramDAO.updateByPrimaryKeySelective(paramInfoEntity);
                updateParamCount++;
                totalParamCount++;
            } else {
                //参数不存在，则创建参数
                paramDAO.insertSelective(ParamInfoEntity.builder()
                                .projectId(machineEntity.getProjectId())
                                .environmentId(machineEntity.getEnvironmentId())
                                .machineId(machineEntity.getMachineId())
                                .paramName(paramObject.getKey())
                                .paramValue(paramObject.getValue())
                                .paramDataType(paramObject.getDataType())
                                .paramType(paramObject.getType())
                                .paramDesc(paramObject.getDesc())
                                .paramExtends(YesOrNo.N.getCode())
                                .paramEncrypt(paramObject.isEncrypt() ? YesOrNo.Y.getCode() : YesOrNo.N.getCode())
                                .paramDynamic(paramObject.isDynamic() ? YesOrNo.Y.getCode() : YesOrNo.N.getCode())
                                .paramSystemProperties(paramObject.isSystemProperties() ? YesOrNo.N.getCode() : YesOrNo.N.getCode())
                                .paramVersion(0)
                                .paramCreator(request.getUserId())
                                .paramStatus(ParamStatusEnum.NORMAL.getCode())
                                .build()
                );
                newParamCount++;
                totalParamCount++;
            }
        }
        return responseBuilder.newFileCount(newFileCount)
                .newParamCount(newParamCount)
                .updateParamCount(updateParamCount)
                .updateFileCount(updateFileCount)
                .totalFileCount(totalFileCount)
                .totalParamCount(totalParamCount)
                .build();
    }
}
